package com.aplink.dellcommercial.webservices;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Handler;

import com.aplink.dellcommercial.configuration.DellController;
import com.aplink.generic.constant.HandlerConstant;
import com.aplink.generic.pojo.ObjPropInfo;
import com.aplink.generic.util.LogUtil;

import org.json.JSONException;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Map;

public class HttpTask extends AsyncTask<Void, Void, DataResult> {

    private static final int TIME_OUT_CONNECTION = 50000;
    private WeakReference<Activity> mActivity;
    private String mDataPost;
    private Handler mHandler;
    private String mHttpUrl;
    private HttpURLConnection mHttpUrlConnection;
    private final LogUtil mLogUtil = new LogUtil( this.getClass() );
    private MethodParams mMethodParam;
    private ArrayList<ObjPropInfo> mObjPropInfos;
    private Map<String, HttpTask> mPoolExcutingTask;
    private ProgressDialog mProgressDialog;
    private String mWaitingDiaLogMessage = "Waiting...";
    private WebServiceHelper mWebServiceHelper;

    public HttpTask( String url, MethodParams params,
                     Map<String, HttpTask> poolExcutingTask, Activity activity,
                     Handler handler, ArrayList<ObjPropInfo> objPropInfos ) {
        this.init( url, params, poolExcutingTask, activity, handler,
                objPropInfos );
    }

    /**
     * @param url
     * @param params
     * @param poolExcutingTask
     * @param datapost
     * @param activity
     * @param handler
     */
    public HttpTask( String url, MethodParams params,
                     Map<String, HttpTask> poolExcutingTask, String datapost,
                     Activity activity, Handler handler ) {
        this.mDataPost = datapost;
        this.init( url, params, poolExcutingTask, activity, handler );
    }

    public HttpTask( String url, MethodParams params,
                     Map<String, HttpTask> poolExcutingTask, String datapost,
                     Activity activity, Handler handler,
                     ArrayList<ObjPropInfo> objPropInfos ) {
        this.mDataPost = datapost;
        this.init( url, params, poolExcutingTask, activity, handler,
                objPropInfos );
    }

    private void init( String url, MethodParams params,
                       Map<String, HttpTask> poolExcutingTask, Activity activity,
                       Handler handler ) {
        this.mHttpUrl = url;
        this.mMethodParam = params;
        this.mPoolExcutingTask = poolExcutingTask;
        this.mActivity = new WeakReference<Activity>( activity );
        this.mHandler = handler;
        this.mWebServiceHelper = WebServiceHelper.getInstance();
    }

    private void init( String url, MethodParams params,
                       Map<String, HttpTask> poolExcutingTask, Activity activity,
                       Handler handler, ArrayList<ObjPropInfo> objPropInfos ) {
        this.mObjPropInfos = objPropInfos;
        this.init( url, params, poolExcutingTask, activity, handler );
    }

    private DataResult request( HttpURLConnection conn, String postData,
                                MethodParams methodParams ) throws SocketTimeoutException,
            FileNotFoundException, IOException, JSONException, ParseException {

        DataResult result;
        if ( postData != null ) {
            this.mLogUtil.info( conn.getURL().toString() + "?" + postData,
                    "request" );
            conn.setRequestMethod( "POST" );
            conn.setDoOutput( true );
            final OutputStream out = new BufferedOutputStream(
                    conn.getOutputStream() );
            out.write( postData.getBytes() );
            out.flush();
            out.close();
        } else {
            this.mLogUtil.info( conn.getURL().toString(), "request" );
            conn.setRequestMethod( "GET" );
        }

        final int httpCode = conn.getResponseCode();
        this.mLogUtil.info( "http response code " + httpCode, "request" );

        if ( ( httpCode == HttpURLConnection.HTTP_OK )
                || ( httpCode == HttpURLConnection.HTTP_CREATED ) ) {
            final InputStream in = new BufferedInputStream(
                    conn.getInputStream() );
            String content = HttpHelper.ConvertStreamToString( in );
            in.close();
            content = content.replace(
                    "<?xml version=\"1.0\" encoding=\"utf-8\"?>", "" );
            content = content.replace( "<string xmlns=\"http://tempuri.org/\">",
                    "" );
            content = content.replace( "</string>", "" );

            result = WebserviceParserDataHelper.getInstance().parse( content,
                    methodParams, this.mObjPropInfos );

            if ( ( result == null ) || methodParams.isOnly4Cache() ) {
                return null;
            }

            result.setMethodParam( methodParams );
            result.setErrorCode( ErrorCode.NO_ERROR );
            return result;
        } else {
            result = new DataResult();
            result.setMethodParam( methodParams );
            result.setErrorCode( ErrorCode.SERVER_ERROR );
        }
        return result;
    }

    @Override
    protected DataResult doInBackground( Void... params ) {
        try {
            if ( ! DellController.isOnline() ) {
                if ( this.mMethodParam.isOnly4Cache() ) {
                    return null;
                }
            }

            System.setProperty( "http.keepAlive", "false" );
            final URL u = new URL( this.mHttpUrl );
            this.mHttpUrlConnection = (HttpURLConnection) u.openConnection();
            this.mHttpUrlConnection.setConnectTimeout( TIME_OUT_CONNECTION );
            this.mHttpUrlConnection.setReadTimeout( TIME_OUT_CONNECTION );
            this.mHttpUrlConnection.setDoInput( true );
            return this.request( this.mHttpUrlConnection, this.mDataPost,
                    this.mMethodParam );
        } catch ( final SocketTimeoutException e ) {
            final DataResult result = new DataResult();
            result.setErrorCode( ErrorCode.TIME_OUT );
            result.setMethodParam( this.mMethodParam );
            this.mLogUtil.error( "Time Out-SocketTimeoutException",
                    "doInBackground", e );
            return result;
        } catch ( final IOException e ) {
            final DataResult result = new DataResult();
            result.setErrorCode( ErrorCode.NETWORK_ERROR );
            result.setMethodParam( this.mMethodParam );
            this.mLogUtil.error( "NETWORK_ERROR-IOException", "doInBackground",
                    e );
            return result;
        } catch ( final JSONException e ) {
            final DataResult result = new DataResult();
            result.setErrorCode( ErrorCode.JSON_PARSE_ERROR );
            result.setMethodParam( this.mMethodParam );
            this.mLogUtil.error( "Json exception", "doInBackground", e );
            return result;
        } catch ( final ParseException e ) {
            final DataResult result = new DataResult();
            result.setErrorCode( ErrorCode.JSON_PARSE_ERROR );
            result.setMethodParam( this.mMethodParam );
            this.mLogUtil.error( "Can not parse Json-ParseException",
                    "doInBackground", e );
            return result;
        } finally {
            if ( this.mHttpUrlConnection != null ) {
                this.mHttpUrlConnection.disconnect();
            }
        }
    }

    @SuppressLint("NewApi")
    @Override
    protected void onPostExecute( DataResult result ) {
        this.mPoolExcutingTask.remove( this.mHttpUrl );
        if ( this.mPoolExcutingTask.size() < WebServiceHelper.MAX_SIZE_POOL ) {
            final HttpTask task = this.mWebServiceHelper.getWaitingTask()
                    .poll();
            if ( task != null ) {
                this.mPoolExcutingTask.put( task.getUrl(), task );
                if ( android.os.Build.VERSION.SDK_INT >= 11 ) {
                    task.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR,
                            (Void) null );
                } else {
                    task.execute( (Void) null );
                }
            }
        }
        if ( this.mProgressDialog != null ) {
            this.mProgressDialog.dismiss();
        }

        if ( this.mHandler != null ) {
            this.mHandler.obtainMessage( HandlerConstant.HTTP_TASK_RESULT,
                    result ).sendToTarget();
        }
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        if ( this.mActivity != null ) {
            this.mProgressDialog = new ProgressDialog( this.mActivity.get() );
            mProgressDialog.setCancelable( false );
            mProgressDialog.setCanceledOnTouchOutside( false );
            this.mProgressDialog.setMessage( this.getWaitingDiaLogMessage() );
            this.mProgressDialog.show();
        }
    }

    public void disconect() {
        if ( this.mHttpUrlConnection != null ) {
            this.mHttpUrlConnection.disconnect();
        }
        this.cancel( true );
        this.mWebServiceHelper.getWaitingTask().add(
                new HttpTask( this.mHttpUrl, this.mMethodParam,
                        this.mPoolExcutingTask, this.mDataPost, this.mActivity
                        .get(), this.mHandler, this.mObjPropInfos ) );

    }

    public String getUrl() {
        return this.mHttpUrl;
    }

    public String getWaitingDiaLogMessage() {
        return this.mWaitingDiaLogMessage;
    }

    public void setWaitingDiaLogMessage( String waitingDiaLogMessage ) {
        this.mWaitingDiaLogMessage = waitingDiaLogMessage;
    }
}
